/**
 * Copyright (c) 1992-2012 singlee.com.cn
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * 
 * $Id: PaginationInterceptor.java 2012-06-07 Ashton.J $
 */
package org.ashton.mybatis.page;

import java.sql.Connection;
import java.util.Properties;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.RowBounds;
import org.ashton.mybatis.dialect.Dialect;
import org.ashton.mybatis.dialect.MySQLDialect;
import org.ashton.mybatis.dialect.OracleDialect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



/**
 * MyBatis分页拦截器，支持MySQL和Oracle <p>
 * 
 * @author  Ashton.J
 * @version v1.0
 */
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class PaginationInterceptor implements Interceptor {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    /** 数据库方言配置文件键值 */
    public static final String DIALECT_KEY = "dialect";
    
    /** 使用的数据库方言 */
    private Dialect dialect;

    /**
     * 拦截原有MyBatis分页查询语句调用，替换执行的sql为物理分页，并移除MyBatis分页设置
     */
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler stmtHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaStmtHandler = MetaObject.forObject(stmtHandler);

        // 判断是否拦截分页
        RowBounds rowBounds = (RowBounds) metaStmtHandler.getValue("delegate.rowBounds");
        if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
            return invocation.proceed();
        }

        String origSql = (String) metaStmtHandler.getValue("delegate.boundSql.sql");
        
        metaStmtHandler.setValue("delegate.boundSql.sql", dialect.getLimitString(origSql, rowBounds.getOffset(), rowBounds.getLimit()));
        metaStmtHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
        metaStmtHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);

        return invocation.proceed();
    }

    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    /**
     * 设置属性
     */
    public void setProperties(Properties properties) {
        String dialect = properties.getProperty(DIALECT_KEY);
        if (dialect == null) {
            throw new RuntimeException("the value of the dialect is not defined");
        }

        Dialect.Type dialectType = Dialect.Type.valueOf(dialect);
        switch (dialectType) {
        case ORACLE:
            this.dialect = new OracleDialect();
            break;
        case MYSQL:
            this.dialect = new MySQLDialect();
            break;
        }
    }

}
